/**
 * 操作 QSPI 的模型类
 * 使用4地址模式进行数据收发
 * 目前支持的芯片:
 * W25Q256FV(正点原子领航者)
 */
#pragma once

#include <stdint.h>
#include <xqspips.h>

#include <array>
#include <vector>

//发送到FLASH器件的指令
#define WRITE_STATUS_CMD 0x01
// #define WRITE_CMD 0x02
#define WRITE_4_ADDR_CMD 0x12
// #define QUAD_WRITE_CMD 0x32
// #define READ_CMD 0x03
// #define WRITE_DISABLE_CMD 0x04
#define READ_STATUS_CMD 0x05
#define WRITE_ENABLE_CMD 0x06
#define FAST_READ_CMD 0x0B
// #define DUAL_READ_CMD 0x3B
// #define QUAD_READ_CMD 0x6B
#define BULK_ERASE_CMD 0xC7// 擦除整个FLASH
#define SEC_ERASE_CMD 0x21 // 扇区擦除,说明书写的20,但无效,且找不到21
#define READ_ID 0x9F

//FLASH BUFFER中各数据的偏移量
#define COMMAND_OFFSET 0// FLASH instruction */
// 内存地址映射的位,从1~4为高位到低位
#define ADDRESS_1_OFFSET 1
#define ADDRESS_2_OFFSET 2
#define ADDRESS_3_OFFSET 3
#define ADDRESS_4_OFFSET 4
// #define DATA_OFFSET 4     // Start of Data for Read/Write */
// #define DUMMY_OFFSET 4    // Dummy byte offset for fast, dual and quad reads
// #define DUMMY_SIZE 1      // Number of dummy bytes for fast, dual and quad reads
#define RD_ID_SIZE 4     // 读取ID, Read ID command + 3 bytes ID response */
#define BULK_ERASE_SIZE 1// Bulk Erase command size */
#define SEC_ERASE_SIZE 5 // Sector Erase command + Sector address */
#define OVERHEAD_SIZE 5  // control information: command and address

// 进入到4地址模式
#define Enter4ByteAddress 0xB7

class QspiDriver {
 public:
  /**
   * 参数来自于flash的芯片手册
   * @param flashSize flash的总大小,7020为0x2000000(32MB),
   * 一般有:0x1000000(16MB),0x2000000(32MB),0x4000000(64MB)
   * @param sectorSize 扇区大小,7020为0x1000(4KB)
   * @param pageSize 页面大小,7020为256字节
   */
  explicit QspiDriver(size_t flashSize = 0x2000000, size_t sectorSize = 0x1000,
                      size_t pageSize = 256);

  /**
   * @param data 将QSPI的数据写入到这个指针中
   * @param size 读取的字节数
   * @param address 起始地址
   */
  void read(void *data, size_t size, uint32_t address = 0);

  /**
   * @param data 从这个指针中读取数据,并写入到 QSPI 中
   * @param size 需要读取的数据字节数
   * @param address 起始地址
   */
  void write(void *data, size_t size, uint32_t address = 0);

  /**
   * 擦除QSPI的扇区
   * @param address 起始地址
   * @param size 需要擦除的字节数
   */
  void sectorErased(uint32_t address, size_t size);

 private:
  /**
   * 初始化
   */
  void initialize();

  /**
   * 初始化 QSPI ID
   * 读取到的数据中,index1是厂商的标志,index2/3是flash大小的标志
   * 可能需要根据不同的厂商来进行不同的配置
   */
  void initID();

  /**
   * 初始化4地址模式
   */
  void initQuadType();

  /**
   * @return true表示QSPI处于忙碌状态,否则为false
   */
  bool isFlashBusy();

 private:
  // qspi的handle
  XQspiPs QspiInstance{};

  // flash的总大小
  size_t flashSize_{};
  // 扇区数
  size_t sectorCount_{};
  // 扇区大小
  size_t sectorSize_{};
  // 页面数
  size_t pageCount_{};
  // 页面大小
  size_t pageSize_{};

  // 指令读取缓存
  uint8_t ReadBuffer[5]{};
};
